package com.changgou.search.service.impl;

import java.util.*;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;

/**
 * 搜索业务层逻辑层
 *
 * @author gaofeng
 * @date 2025年04月23日 17:20
 */
@Service("skuServiceImpl")
public class SkuServiceImpl implements SkuService {
    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public Map<String, Object> query(Map<String, String> searchParams) {
        NativeSearchQuery searchQuery = buildNativeQueryBuilder(searchParams);
        AggregatedPage<SkuInfo> skuInfoPage =
            elasticsearchTemplate.queryForPage(searchQuery, SkuInfo.class, new SearchResultMapperImpl());
        if (skuInfoPage == null) {
            return Collections.emptyMap();
        }

        Map<String, Object> result = new HashMap<>();
        result.put("rows", skuInfoPage.getContent());
        result.put("total", skuInfoPage.getTotalElements());
        result.put("totalPage", skuInfoPage.getTotalPages());
        result.put("categoryList", getCategoryNames(skuInfoPage));
        result.put("brandList", getBrandNames(skuInfoPage));
        result.put("specList", getSpecs(skuInfoPage));
        Pageable pageable = searchQuery.getPageable();
        result.put("pageNumber", pageable.getPageNumber());
        result.put("pageSize", pageable.getPageSize());
        return result;
    }

    private NativeSearchQuery buildNativeQueryBuilder(Map<String, String> searchParams) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        // 设置高亮的字段 针对 商品的名称进行高亮
        queryBuilder.withHighlightFields(new HighlightBuilder.Field("name"));
        // 设置前缀 和 后缀
        queryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<em style=\"color:red\">").postTags("</em>"));

        // 默认每页10条
        // 当用户选择了分类，将分类作为搜索条件，则不需要对分类进行分组搜索，因为分组搜索的结果是用于显示分类搜索条件的
        // if (CollectionUtils.isEmpty(searchParams) || StringUtils.isBlank(searchParams.get("category"))) {
        queryBuilder.addAggregation(AggregationBuilders.terms("skuCategoryGroup").field("categoryName").size(100));
        // }

        // 当用户选择了品牌，将品牌作为搜索条件，则不需要对品牌进行分组搜索，因为分组搜索的结果是用于显示品牌搜索条件的
        // if (CollectionUtils.isEmpty(searchParams) || StringUtils.isBlank(searchParams.get("brand"))) {
        queryBuilder.addAggregation(AggregationBuilders.terms("skuBrandGroup").field("brandName").size(100));
        // }

        queryBuilder.addAggregation(AggregationBuilders.terms("skuSpecGroup").field("spec.keyword").size(100));
        String keywords = searchParams.get("keywords");
        if (StringUtils.isEmpty(keywords)) {
            keywords = "小米";
        }

        queryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords, "name", "categoryName", "brandName"));

        String sortField = searchParams.get("sortField");
        String sortRule = searchParams.get("sortRule");
        if (StringUtils.isNotBlank(sortField) && StringUtils.isNotBlank(sortRule)) {
            queryBuilder.withSort(SortBuilders.fieldSort(sortField)
                .order("ASC".equalsIgnoreCase(sortRule) ? SortOrder.ASC : SortOrder.DESC));
        }

        int pageNum = getPageNum(searchParams);
        int defaultPageSize = 30;
        queryBuilder.withPageable(PageRequest.of(pageNum - 1, defaultPageSize));
        queryBuilder.withFilter(buildBoolQueryBuilder(searchParams));

        return queryBuilder.build();
    }

    private BoolQueryBuilder buildBoolQueryBuilder(Map<String, String> searchParams) {
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        addTermQuery(boolQueryBuilder, "name", searchParams.get("name"));
        addTermQuery(boolQueryBuilder, "categoryName", searchParams.get("category"));
        addTermQuery(boolQueryBuilder, "brandName", searchParams.get("brand"));

        for (Map.Entry<String, String> searchKey : searchParams.entrySet()) {
            String key = searchKey.getKey();
            if (!key.startsWith("spec_")) {
                continue;
            }

            String spec = key.substring(key.indexOf('_') + 1) + ".keyword";
            addTermQuery(boolQueryBuilder, "specMap." + spec, searchKey.getValue());
        }

        String price = searchParams.get("price");
        if (StringUtils.isNotBlank(price)) {
            addPriceRangeQuery(boolQueryBuilder, price);
        }

        return boolQueryBuilder;
    }

    private int getPageNum(Map<String, String> searchParams) {
        String pageNum = searchParams.get("pageNum");
        if (StringUtils.isBlank(pageNum)) {
            return 1;
        }

        try {
            int page = Integer.parseInt(pageNum);
            return Math.max(page, 1);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        return 1;
    }

    private void addPriceRangeQuery(BoolQueryBuilder boolQueryBuilder, String price) {
        String cleanedPrice = price.replace("以上", "").replace("元", "");
        String[] prices = cleanedPrice.split("-");
        if (prices.length == 2) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(prices[0]).to(prices[1]));
        } else if (prices.length == 1) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(prices[0]));
        }
    }

    private void addTermQuery(BoolQueryBuilder boolQueryBuilder, String field, String value) {
        if (StringUtils.isNotBlank(value)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery(field, value));
        }
    }

    private List<String> getCategoryNames(AggregatedPage<SkuInfo> skuInfoPage) {
        if (skuInfoPage == null) {
            return Collections.emptyList();
        }

        StringTerms skuCategory = (StringTerms)skuInfoPage.getAggregation("skuCategoryGroup");
        if (skuCategory == null) {
            return Collections.emptyList();
        }

        List<String> categoryNames = new ArrayList<>();
        for (StringTerms.Bucket bucket : skuCategory.getBuckets()) {
            categoryNames.add(bucket.getKeyAsString());
        }

        return categoryNames;
    }

    private List<String> getBrandNames(AggregatedPage<SkuInfo> skuInfoPage) {
        if (skuInfoPage == null) {
            return Collections.emptyList();
        }

        StringTerms skuBrand = (StringTerms)skuInfoPage.getAggregation("skuBrandGroup");
        if (skuBrand == null) {
            return Collections.emptyList();
        }

        List<String> brandNames = new ArrayList<>();
        for (StringTerms.Bucket bucket : skuBrand.getBuckets()) {
            brandNames.add(bucket.getKeyAsString());
        }

        return brandNames;
    }

    private Map<String, Set<String>> getSpecs(AggregatedPage<SkuInfo> skuInfoPage) {
        if (skuInfoPage == null) {
            return Collections.emptyMap();
        }

        StringTerms skuSpec = (StringTerms)skuInfoPage.getAggregation("skuSpecGroup");
        if (skuSpec == null) {
            return Collections.emptyMap();
        }

        List<String> specList = new ArrayList<>();
        for (StringTerms.Bucket bucket : skuSpec.getBuckets()) {
            specList.add(bucket.getKeyAsString());
        }

        Map<String, Set<String>> specMap = new HashMap<>();
        for (String string : specList) {
            Map<String, String> map = JSON.parseObject(string, Map.class);
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String specKey = entry.getKey();
                String spec = entry.getValue();

                if (specMap.containsKey(specKey)) {
                    Set<String> specs = specMap.get(specKey);
                    specs.add(spec);
                } else {
                    Set<String> specs = new TreeSet<>();
                    specs.add(spec);
                    specMap.put(specKey, specs);
                }
            }
        }

        return specMap;
    }
}
